""" pip install aliyun-python-sdk-core  # 安装AcsClient依赖‌
    pip install aliyun-python-sdk-iot   # 支持IoT服务端API调用"""

import json
import base64
import time
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.request import CommonRequest
from aliyunsdkiot.request.v20180120 import QueryDeviceDetailRequest, PubRequest, GetDeviceStatusRequest
from asgiref.sync import async_to_sync
from app01.models import DeviceData
from django.utils import timezone


# ################################ 上报给设备 ################################
def submit(message, channel_layer_manager):
    from app01 import models
    formData = message['formData']  # 要发送的数据
    IotData = message['IotData']  # 设备各项明细
    checkboxData = message['checkboxData']  # 哪些是要发送的数据
    formBak = {}
    for item in checkboxData:
        index = item['index']
        checked = item['checked']
        if checked:  # 过滤不要发送的数据
            # 获取键和值
            key = list(formData.keys())[index]
            value = formData[key]
            # 添加到目标字典
            formBak[key] = value
    formData = formBak

    # 确认是哪种数据类型,转换后,再给平台发送
    # 1. 从设备数据库中查找到此设备的关联产品的toID(正向查询)
    device_name = IotData.get('device_name')
    device_name_alias = IotData.get('device_name_alias')
    device_secret = IotData.get('device_secret')
    device = models.Device.objects.filter(device_name=device_name,
                                          device_name_alias=device_name_alias,
                                          device_secret=device_secret,
                                          ).first()
    product = device.toProduct
    # 2. 从产品的toID查找到此产品关联的功能定义(反向查询)
    funDefine = product.fundefine_set.all()
    # 3.从formData中找跟funDefine匹配的功能定义数据类型,转为对应的数据类型,再传给阿里物联网平台
    for key, value in formData.items():
        for item in funDefine:
            Identifier = item.Identifier
            dataType = item.dataType
            if Identifier == key and dataType <= 5:
                formData[key] = convert_to_number(value)
    # print('formData:', formData)
    # payload = "{'params':%s}" % formData
    # [0]id, [1]enable是否被管理, [2]accessKey进入的是哪个平台, [3]accessSecret进入平台密钥,
    # [4]iotInstanceId实例ID, [5]iotInstanceId实例ID别名, [6]clientId客户端ID, [7]host_name主机名,
    # [8]consumerGroupId消费组, [9]product_key产品ID, [10]product_key_alias产品ID别名,
    # [11]device_name设备名, [12]device_name_alias设备名别名, [13]device_secret设备密钥
    ak = IotData['accessKey']  # 这个要的：ak='<your_access_key_id>'
    secret = IotData['accessSecret']  # 这个要的：secret='<your_access_secret>'
    iot = IotData['iotInstanceId']  # 这个要的IotInstanceId
    region_id = IotData['host_name']  # 这个要的region_id
    product_key = IotData['product_key']  # 这个要的产品key
    device_name = IotData['device_name']  # 这个要的设备name
    toDevice_id = models.Device.objects.get(device_name=device_name).id

    # 初始化客户端
    client = AcsClient(ak=ak, secret=secret, region_id=region_id)

    # 查询设备状态
    status = get_device_status(product_key, device_name, iot, client)
    print(f"设备状态: {status}")
    if status['status'] == 'OFFLINE':
        sendOrReceive = 2
    else:
        sendOrReceive = 1
    formData_items = transform_data(formData)  # 转为需要的格式存储
    # 保存到数据库
    device_data = DeviceData(
        # deviceType='WifiTemplate',  # 根据业务需求设置
        # iotId=secret,   # 阿里与设备通信的唯一标识,要单独获取
        productKey=product_key,
        deviceName=device_name,
        items=json.dumps(formData_items),  # 序列化字典
        # gmtCreate=timezone.now().strftime('%Y-%m-%d %H:%M:%S'),
        gmtCreate=str(int(time.time() * 1000)),  # 毫秒级别
        toDevice_id=models.Device.objects.get(device_name=device_name).id,
        sendOrReceive=sendOrReceive
    )
    device_data.save()

    # 发送控制指令:方案1（若设备在不在线都会成功，关键平台上是否有设置消息留存）
    if send_device_command(product_key, device_name, formData, iot, client):
        print("已成功上报给设备")

    # 发送控制指令：方案2 （若设备不在线则直接返回失败）
    # success = rrpc_command(product_key, device_name, formData, iot, client)
    # if success:
    #     print(f"设备在线，指令已被设备确认: {success}")  # 设备在线时返回true
    # else:
    #     print(f"设备离线，指令不被设备确认: {success}")  # 设备离线时返回False

    # # 发送到共享队列，给前端发送过去,更新页面(显示是否发布成功)
    data8 = json.dumps({'stateMid': 2000, 'status': status['status']})  # 字典需要序列化成json字符串
    async_to_sync(channel_layer_manager.channel_layer.group_send)(
        device_name,  # group组
        {
            "type": "send.data",  # 去调用send_data()函数
            "message": data8,  # 在send_data()里发送data8数据
        }
    )


# 把上报设备的数据转为需要的存储格式
def transform_data(input_dict):
    timestamp = int(time.time() * 1000)  # 获取毫秒级时间戳
    return {
        key: {
            "value": value,
            "time": timestamp
        } for key, value in input_dict.items()
    }


def get_device_status(product_key, device_name, iot, client):
    """查询设备详情,确定是否在线"""
    request = QueryDeviceDetailRequest.QueryDeviceDetailRequest()
    request.set_ProductKey(product_key)
    request.set_DeviceName(device_name)
    # 若使用独有实例，添加IotInstanceId参数
    request.add_query_param('IotInstanceId', iot)
    try:
        response = client.do_action_with_exception(request)
        data = json.loads(response)
        return {
            'status': data['Data']['Status'],
            # 'last_online': data['Data']['GmtModified']
        }
    except Exception as e:
        print(f"查询失败: {str(e)}")
        return None


def send_device_command(product_key, device_name, command, iot, client):
    """下发给设备指令"""
    request = PubRequest.PubRequest()
    # 若使用独有实例，添加IotInstanceId参数
    request.add_query_param('IotInstanceId', iot)
    request.set_ProductKey(product_key)
    request.set_TopicFullName(f"/sys/{product_key}/{device_name}/thing/service/property/set")

    # 关键修改：增加Base64编码
    raw_message = json.dumps(command, separators=(",", ":"))  # 移除冒号后空格
    # print('raw_message:', raw_message)
    encoded_message = base64.b64encode(raw_message.encode('utf-8')).decode('utf-8')
    request.set_MessageContent(encoded_message)  # 替换为编码后的内容

    try:
        response = client.do_action_with_exception(request)
        return json.loads(response)['Success']
    except Exception as e:
        print(f"指令发送失败: {str(e)}")
        return False


def rrpc_command(product_key, device_name, command, iot, client):
    """同步指令下发（设备必须在线）"""
    request = CommonRequest()
    region = 'cn-shanghai'
    request.set_domain(f'iot.{region}.aliyuncs.com')
    request.set_version('2018-01-20')
    request.set_action_name('RRpc')
    request.add_query_param('ProductKey', product_key)
    request.add_query_param('DeviceName', device_name)
    request.add_query_param('RequestBase64Byte', base64.b64encode(json.dumps(command).encode()).decode())

    try:
        response = client.do_action_with_exception(request)
        return json.loads(response)['Success']
    except Exception as e:
        if "TIMEOUT" in str(e):
            print("设备无响应，判定为离线")
        return False


# ############################## 字符串转数字 ####################################################
def convert_to_number(s):
    # if s.lstrip().startswith('-'):
    #     return s
    try:
        return int(s)
    except ValueError:
        try:
            return float(s)
        except ValueError:
            try:
                return complex(s)
            except ValueError:
                # print('s:', s)
                return 0  # 无法转换，返回None或其他指示失败的值
